#include "power.h"

#include <stdint.h>

#include "acommon_err.h"
#include "bsp.h"

#ifdef DIR_DETR_PORT
    #define dir_detr_get_state() Gpio_GetInputIO(DIR_DETR_PORT, DIR_DETR_PIN)
#endif

typedef struct
{
    uint8_t in_left_right : 2;
    uint8_t drv_cnt       : 6;

    union
    {
        struct
        {
            uint8_t isolation_left  : 1;
            uint8_t isolation_right : 1;
        };
        uint8_t isolation : 2;
    };

    uint16_t vcap_voltage;
} power_direction_t;

static volatile power_direction_t power_direction = {0};

#define power_delay_ms(x) bsp_delay_ms(x)

/**
 * @brief 初始化电源管理GPIO
 *
 */
void power_init(void)
{
    stc_gpio_cfg_t stcGpioCfg;

    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);

    DDL_ZERO_STRUCT(stcGpioCfg);
    stcGpioCfg.enDir      = GpioDirOut;
    stcGpioCfg.enDrv      = GpioDrvH;
    stcGpioCfg.enPu       = GpioPuDisable;
    stcGpioCfg.enPd       = GpioPdDisable;
    stcGpioCfg.enOD       = GpioOdDisable;
    stcGpioCfg.enCtrlMode = GpioAHB;

    Gpio_Init(DIR_DRV_PORT, DIR_DRV_PIN, &stcGpioCfg);
    Gpio_Init(LOAD_PORT, LOAD_PIN, &stcGpioCfg);

    stcGpioCfg.enOD = GpioOdEnable;
    Gpio_Init(FETL_PORT, FETL_PIN, &stcGpioCfg);
    Gpio_Init(FETR_PORT, FETR_PIN, &stcGpioCfg);

    stcGpioCfg.enDir = GpioDirIn;
    stcGpioCfg.enOD  = GpioOdDisable;
    Gpio_Init(DIR_DETL_PORT, DIR_DETL_PIN, &stcGpioCfg);

    /* 初始化RX1, 用于来电方向检测 */
    Gpio_Init(RX_1_PORT, RX_1_PIN, &stcGpioCfg); // RX_START_PIN
    Gpio_Init(RX_2_PORT, RX_2_PIN, &stcGpioCfg); // RX_START_PIN

#ifdef DIR_DETR_PORT
    Gpio_Init(DIR_DETR_PORT, DIR_DETR_PIN, &stcGpioCfg);
#endif
    ACOMMON_LOGD("power gpio init");

    load_off();
    power_isolation_all();
}

void power_on(void)
{
    ACOMMON_LOGD("power on");
    power_direction.isolation = POWER_ISOLATION_NORMAL;

    fetr_on();
    fetl_on();
}

void power_on_in_dir(void)
{
    fetl_off();
    fetr_off();
    power_set_direction();
}

void power_right_off(void)
{
    ACOMMON_LOGD("power right off");
    fetr_off();
}

void power_reset_direction(void)
{
    if (POWER_IN_LEFT == power_direction.in_left_right)
    {
        ACOMMON_LOGD("power right off");
        fetr_off();
    }
    else if (POWER_IN_RIGHT == power_direction.in_left_right)
    {
        ACOMMON_LOGD("power left off");
        fetl_off();
    }
    else
    {
        ACOMMON_LOGD("power right off, default");
        fetr_off();
    }
}

void power_set_direction(void)
{
    if (POWER_IN_LEFT == power_direction.in_left_right)
    {
        ACOMMON_LOGD("power in left");
        fetl_on();
        power_direction.isolation_left = 0;
    }
    else if (POWER_IN_RIGHT == power_direction.in_left_right)
    {
        ACOMMON_LOGD("power in right");
        fetr_on();
        power_direction.isolation_right = 0;
    }
    else
    {
        ACOMMON_LOGD("power in left, default");
        fetl_on();
        power_direction.isolation_left = 0;
    }
}

void power_set_drv_on(void)
{
    dir_drv_on();
    power_direction.in_left_right = POWER_IN_OFF;
    power_direction.drv_cnt       = 0;
}

int power_check_direction(void)
{
    //
    dir_drv_on();
#if 1
    if (dir_detl_get_state() == FALSE)
    {
        if (power_direction.in_left_right == POWER_IN_LEFT) { power_direction.drv_cnt++; }
        else power_direction.drv_cnt = 0;

        power_direction.in_left_right = POWER_IN_LEFT;
    }
    else if (Gpio_GetInputIO(RX_1_PORT, RX_1_PIN))
    {
        if (power_direction.in_left_right == POWER_IN_RIGHT) { power_direction.drv_cnt++; }
        else { power_direction.drv_cnt = 0; }

        power_direction.in_left_right = POWER_IN_RIGHT;
        // ACOMMON_LOGD("power in %d", power_direction.in_left_right);
    }
    else
    {
        power_direction.drv_cnt       = 0;
        power_direction.in_left_right = POWER_IN_OFF;
    }
#else
    if (dir_detl_get_state() == FALSE) power_direction.in_left_right = POWER_IN_LEFT;
    else power_direction.in_left_right = POWER_IN_RIGHT;
    power_direction.drv_cnt = 6;
#endif
    // dir_drv_off();

    if (power_direction.drv_cnt > 5) { return power_direction.in_left_right; }
    else return POWER_IN_OFF;
}

int power_in_direction(void)
{
    int ret = POWER_IN_OFF;
    dir_drv_on();
    power_delay_ms(1);

    if (dir_detl_get_state() == FALSE)
    {
        ACOMMON_LOGD("power in left");
        fetl_on();
        ret                            = POWER_IN_LEFT;
        power_direction.isolation_left = 0;
    }
#ifdef DIR_DETR_PORT
    if (dir_detr_get_state() == FALSE)
    {
        ACOMMON_LOGD("power in right");
        fetr_on();
    }
#else
    else
    {
        uint8_t rx_state = Gpio_GetInputIO(RX_1_PORT, RX_1_PIN);

        if (rx_state == 0)
        {
            ACOMMON_LOGD("power in left, default");
            fetl_on();
            ret                            = POWER_IN_LEFT;
            power_direction.isolation_left = 0;
        }
        else
        {
            ACOMMON_LOGD("power in right, default");
            fetr_on();
            ret                             = POWER_IN_RIGHT;
            power_direction.isolation_right = 0;
        }
    }
#endif
    dir_drv_off();

    power_direction.in_left_right = ret;

    return ret;
}

static int power_try_rx1_state(void)
{
    volatile uint8_t ack = 0;
    // dir_drv_on();
    for (int i = 0; i < 10; i++)
    {
        power_delay_ms(1);

        /* 持续 0V 4ms 标识短路, 2 * 2ms */
        if (!Gpio_GetInputIO(RX_1_PORT, RX_1_PIN))
        {
            ack++;
            if (ack > 4)
            {
                // dir_drv_off();
                return 0;
            }
        }
        else { ack = 0;}
    }
    // dir_drv_off();
    return 1;
}

int power_try_peer(void)
{
    int ret = 0;

    if (power_direction.isolation_left)
    {
        ACOMMON_LOGD("loopbus check left");
        fetl_on();
        if (!power_try_rx1_state())
        {
            fetl_off();
            ACOMMON_LOGI("loopbus left Short Circuit");
            power_direction.isolation_left = 1;
            ret                            = 1;
        }
        else { power_direction.isolation_left = 0; }
    }
    if (power_direction.isolation_right)
    {
        ACOMMON_LOGD("loopbus check right");
        fetr_on();
        // power_delay_ms(2);
        if (!power_try_rx1_state())
        {
            fetr_off();
            ACOMMON_LOGI("loopbus right Short Circuit");
            power_direction.isolation_right = 1;
            ret                             = 1;
        }
        else { power_direction.isolation_right = 0; }
    }

    return ret;
}

uint8_t power_isolation_state(void) { return power_direction.isolation; }

void power_isolation_state_set(uint8_t state) { power_direction.isolation = state; }

uint8_t power_get_direction(void) { return power_direction.in_left_right; }

void power_isolation_all(void)
{
    dir_drv_off();
    fetl_off();
    fetr_off();
    power_direction.isolation     = POWER_ISOLATION_LEFT | POWER_ISOLATION_RIGHT;
    power_direction.in_left_right = POWER_IN_OFF;
    power_direction.drv_cnt       = 0;
}

/**
 * Vin = Vref / (1 << 12) * ADC * K,  K = (R1 + R2) / R1
 * Vref = 3.3V
 * K = (6.8M + 680K) / 680K = 11
 * Vcap = 3300 / 4096 * 11 * ADC = 8.862304 * ADC
 **/
#define ADC_TO_VCAP_VOLTAGE(x) (x * 8.862304)
uint16_t power_get_vcap_voltage(void)
{
    *(uint32_t *) (&M0P_GPIO->PAADS + AD_VCAP_PORT) |= (1 << AD_VCAP_PIN);

    M0P_SYSCTRL->PERI_CLKEN_f.ADC                    = 1;

    M0P_BGR->CR_f.BGR_EN                             = 1;
    M0P_ADC->CR0                                     = 1; ///< ADC 使能
    bsp_delay_us(20);

    /**
     * ADC采样配置:
     * 1. 时钟1分频, 时钟频率: PCLK = 4MHz
     * 2. 采用AVCC电压, 3.3V
     * 3. 使能内部放大器, 最大转换速率: 200KHz
     * 4. 12个采样周期, 转换速率 = 4MHz / (12 + 16) = 142KHz
     */
    M0P_ADC->CR0          |= AdcMskSampCycle12Clk | AdcMskBufEnable | AdcMskRefVolSelAVDD | AdcMskClkDiv1;

    M0P_ADC->CR1_f.MODE    = AdcSglMode;    ///< single mode
    M0P_ADC->CR1_f.ALIGN   = AdcAlignRight; ///< Align Right

    // power_delay_ms(20);

    M0P_ADC->CR0_f.SGLMUX  = AD_VACP_CH4;
    M0P_ADC->ICR          &= ~(uint32_t) AdcMskIrqSgl;  // clear the irq
    M0P_ADC->SGLSTART      = 1u;                        // ADC start
    while (TRUE != (M0P_ADC->IFR & AdcMskIrqSgl)) { ; } // waiting for conversion
    uint16_t tmp                  = (uint16_t) M0P_ADC->RESULT;
    power_direction.vcap_voltage  = (uint16_t) ADC_TO_VCAP_VOLTAGE(tmp);
    M0P_ADC->SGLSTART             = 0u; // ADC stop

    M0P_BGR->CR_f.BGR_EN          = 0;  // disable BGR
    M0P_ADC->CR0_f.EN             = 0u; // disable ADC
    M0P_SYSCTRL->PERI_CLKEN_f.ADC = 0;

    return power_direction.vcap_voltage;
}
